﻿/*
 * CoordinateProcessor
 * 
 * initial implementation : 20.11.2008
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 * 
 * for license see Petra.Gis.License.txt
 */
using System;
using System.Collections.Generic;
using Petra.Gis.Geometry;
using Petra.Gis.Geometry.Support;


namespace Petra.Gis.Geometry.Utils
{


  public partial class CoordinateProcessor
  {


    //public delegate double processCoord(double ordinate, int args);


    //public CoordinateProcessor(processCoord fn)
    //{
      
    //}



    public IProcessCoordinate ProcessCoordinate
    { get; set; }


    /// <summary>
    /// Transforms a <see cref="Petra.Gis.Geometry.Support.BoundingBox"/>.
    /// </summary>
    /// <param name="box">BoundingBox to transform</param>
    /// <returns>Transformed object</returns>
    public BoundingBox TransformBox(BoundingBox box)
    {
      if (box == null)
        return null;
      CoordinateList corners = new CoordinateList(4, Dimension.dim3D);
      corners[0] = this.Transform(box.Min); //LL
      corners[1] = this.Transform(box.Max); //UR
      corners[2] = this.Transform(new CoordStruct(box.Min.X, box.Max.Y)); //UL
      corners[3] = this.Transform(new CoordStruct(box.Max.X, box.Min.Y)); //LR

      return corners.getEnvelope();
    }




    /// <summary>
    /// Transforms a <see cref="Petra.Gis.Geometry.GeometryBase"/>.
    /// </summary>
    /// <param name="g">Geometry to transform</param>
    /// <returns>Transformed object</returns>
    public GeometryBase Transform(GeometryBase g)
    {
      if (g == null)
        return null;
      else if (g is Point)
        return TransformPoint(g as Point);
      else if (g is LineString)
        return TransformLineString(g as LineString);
      else if (g is Polygon)
        return TransformPolygon(g as Polygon);
      else if (g is MultiPoint)
        return TransformMultiPoint(g as MultiPoint);
      else if (g is MultiLine)
        return TransformMultiLine(g as MultiLine);
      else if (g is MultiArea)
        return TransformMultiPolygon(g as MultiArea);
      else if(g is GeometryCollection)
        return TransformGeometryCollection(g as GeometryCollection);
      else
        throw new ArgumentException("Could not transform geometry type '" + g.GetType().ToString() + "'");
    }




    /// <summary>
    /// Transforms a <see cref="Petra.Gis.Geometry.Point"/>.
    /// </summary>
    /// <param name="p">Point to transform</param>
    /// <returns>Transformed object</returns>
    public Point TransformPoint(Point p)
    {
      this.Transform(p.coordList);
      return p;
    }



    /// <summary>
    /// Transforms a <see cref="Petra.Gis.Geometry.LineString"/>.
    /// </summary>
    /// <param name="l">LineString to transform</param>
    /// <returns>Transformed object</returns>
    public LineString TransformLineString(LineString l)
    {
      this.Transform(l.coordList);
      return l;
    }




    /// <summary>
    /// Transforms a <see cref="Petra.Gis.Geometry.LinearRing"/>.
    /// </summary>
    /// <param name="r">LinearRing to transform</param>
    /// <returns>Transformed object</returns>
    public LineRing TransformLinearRing(LineRing r)
    {
      this.Transform(r.coordList);
      return r;
    }




    /// <summary>
    /// Transforms a <see cref="Petra.Gis.Geometry.Polygon"/>.
    /// </summary>
    /// <param name="p">Polygon to transform</param>
    /// <returns>Transformed object</returns> 
    public Polygon TransformPolygon(Polygon p)
    {
      foreach (LineRing r in p)
        TransformLinearRing(r);

      return p;

    }




    /// <summary>
    /// Transforms a <see cref="Petra.Gis.Geometry.MultiPoint"/>.
    /// </summary>
    /// <param name="points">MultiPoint to transform</param>
    /// <returns>Transformed object</returns>
    public MultiPoint TransformMultiPoint(MultiPoint points)
    {
      foreach (Point p in points)
        this.Transform(p.coordList);

      return points;
    }




    /// <summary>
    /// Transforms a <see cref="Petra.Gis.Geometry.MultiLineString"/>.
    /// </summary>
    /// <param name="lines">MultiLineString to transform</param>
    /// <returns>Transformed object</returns>
    public MultiLine TransformMultiLine(MultiLine lines)
    {
      foreach (LineString l in lines)
        this.Transform(l.coordList);

      return lines;
    }




    /// <summary>
    /// Transforms a <see cref="Petra.Gis.Geometry.MultiPolygon"/>.
    /// </summary>
    /// <param name="polys">MultiPolygon to transform</param>
    /// <returns>Transformed object</returns>
    public MultiArea TransformMultiPolygon(MultiArea polys)
    {

      foreach (Polygon p in polys)
        this.TransformPolygon(p);

      return polys;
    }




    /// <summary>
    /// Transforms a <see cref="Petra.Gis.Geometry.GeometryCollection"/>.
    /// </summary>
    /// <param name="geoms">GeometryCollection to transform</param>
    /// <returns>Transformed object</returns>
    public GeometryCollection TransformGeometryCollection(GeometryCollection geoms)
    {
      foreach (GeometryBase g in geoms)
        this.Transform(g);

      return geoms;
    }




    /// <summary>
    /// transforms single Coordinate from source to target coordinate system
    /// </summary>
    /// <param name="from">Coordinate to transform</param>
    public virtual void Transform(Coordinate coord)
    {
      //transform(from, Dimension.dim3D);
      transform(coord);
    }




    /// <summary>
    /// transforms single Coordinate from source to target coordinate system
    /// </summary>
    /// <param name="from">Coordinate to transform</param>
    public virtual void Transform(ICoordinate coord)
    {
      //transform(from, Dimension.dim3D);
      transform(coord);
    }





    /// <summary>
    /// transforms input CoordinateList coordinates from source to target coordinate system
    /// </summary>
    /// <param name="coordList">CoordinateList to transform</param>
    public virtual void Transform(CoordinateList coordList)
    {

      foreach (ICoordinate coord in coordList)
      {
        //transform(from, coordList.dimension);
        transform(coord);
      }

    }




    /// <summary>
    /// Transforms single CoordStruct structure from source to destination coordinate system
    /// This function (only) must return value as CoordStruct is structure.
    /// </summary>
    /// <param name="point">point to transform</param>
    /// <returns>new instance of <c>CoordStruct</c> with transformed coordinates</returns>
    public virtual CoordStruct Transform(CoordStruct point) //by val default
    {
      // input CoordStruct is passed by value
      // variable point is therefore local copy
      Coordinate tmp = new Coordinate(point.X, point.Y, point.Z);

      transform(tmp);

      point.X = tmp.X;
      point.Y = tmp.Y;
      point.Z = tmp.Z;

      //CoordStruct is structure, so return value is more logical here...
      return point;

    }





    protected void transform(ICoordinate coord)
    {
      //if (ProcessCoordFunct == null)
      //  return;

      //ProcessCoordFunct(coord.X);
      //ProcessCoordFunct(coord.Y);
      //ProcessCoordFunct(coord.Z);

      ProcessCoordinate.Transform(coord);
    }



  }
}
